package com.katze.boot.plugins.redis.operations;

import com.katze.boot.plugins.redis.RedisTemplateManager;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;

/**
 * 说  明：Redis 对Key-Value-Score操作的封装
 * 作  者：zf.zeng
 * 日  期：2021-02-14 (星期日)
 **/
public class RedisZSetOperations<V>{

    private final ZSetOperations<String, V> zSetOperations;
    private RedisTemplateManager<V> manager;

    public RedisZSetOperations(RedisTemplate<String, V> redisTemplate){
        this.zSetOperations = redisTemplate.opsForZSet();
    }

    public RedisZSetOperations(RedisTemplateManager<V> manager){
        this.manager = manager;
        this.zSetOperations = manager.primary().opsForZSet();
    }

    public <R>R select(int database, Function<ZSetOperations<String, V>, R> function){
        return function.apply(database < 0? this.zSetOperations:this.manager.select(database).opsForZSet());
    }

    /**
     * 添加元素同时指定元素的分值
     * @param key 键
     * @param value 值
     * @param score 分值（排序）
     */
    public Boolean add(String key, V value, double score){
        return zSetOperations.add(key, value, score);
    }

    /**
     * 批量添加元素同时指定元素的分值
     * @param key 键
     * @param tuples 值
     */
    public Long add(String key, Set<ZSetOperations.TypedTuple<V>> tuples){
        return zSetOperations.add(key, tuples);
    }

    /**
     * 获取变量中元素的索引(下标开始位置为0,从小到大排序)
     * @param key 键
     * @param value 值
     */
    public Long rank(String key, Object value){
        return zSetOperations.rank(key, value);
    }

    /**
     * 获取索引区间内的排序结果集合(从0开始,从小到大,只有列名)
     * @param key 键
     * @param start 开始位置
     * @param end 结束位置
     */
    public Set<V> range(String key, long start, long end){
        return zSetOperations.range(key, start, end);
    }

    /**
     * 获取分数范围内的 [min,max] 的排序结果集合 (从小到大,只有列名)
     * @param key 键
     * @param min 最小分值
     * @param max 最大分值
     */
    public Set<V> rangeByScore(String key, double min, double max){
        return zSetOperations.rangeByScore(key, min, max);
    }

    /**
     * 返回 分数范围内 指定 count 数量的元素集合, 并且从 offset 下标开始(从小到大,不带分数的集合)
     * @param key 键
     * @param min 最小分值
     * @param max 最大分值
     * @param offset 从指定下标开始
     * @param count 输出指定元素数量
     */
    public Set<V> rangeByScore(String key, double min, double max,long offset,long count){
        return zSetOperations.rangeByScore(key, min, max, offset, count);
    }

    /**
     * 获取索引区间内的排序结果集合(从0开始,从小到大,带上分数)
     * @param key 键
     * @param start 开始位置
     * @param end 结束位置
     */
    public Set<ZSetOperations.TypedTuple<V>> rangeWithScores(String key, long start, long end){
        return zSetOperations.rangeWithScores(key, start, end);
    }

    /**
     * 获取分数范围内的 [min,max] 的排序结果集合 (从小到大,集合带分数)
     * @param key 键
     * @param min 最小分值
     * @param max 最大分值
     */
    public Set<ZSetOperations.TypedTuple<V>> rangeByScoreWithScores(String key, double min, double max){
        return zSetOperations.rangeByScoreWithScores(key, min, max);
    }

    /**
     * 返回 分数范围内 指定 count 数量的元素集合, 并且从 offset 下标开始(从小到大,带分数的集合)
     * @param key 键
     * @param min 最小分值
     * @param max 最大分值
     * @param offset 从指定下标开始
     * @param count 输出指定元素数量
     */
    public Set<ZSetOperations.TypedTuple<V>> rangeByScoreWithScores(String key, double min, double max, long offset, long count){
        return zSetOperations.rangeByScoreWithScores(key, min, max, offset, count);
    }

    /**
     * 获取倒序排列的索引值
     * @param key 键
     * @param value 值
     */
    public Long reverseRank(String key, Object value){
        return zSetOperations.reverseRank(key, value);
    }

    /**
     * 获取索引区间内的排序结果集合(从0开始,从大到小,只有列名)
     * @param key  键
     * @param start 开始下标
     * @param end 结束下标
     */
    public Set<V> reverseRange(String key, long start, long end){
        return zSetOperations.reverseRange(key, start, end);
    }

    /**
     * 获取分数范围内的 [min,max] 的排序结果集合 (从大到小,集合不带分数)
     * @param key 键
     * @param min 最小分值
     * @param max 最大分值
     */
    public Set<V> reverseRangeByScore(String key,double min,double max){
        return zSetOperations.reverseRangeByScore(key, min, max);
    }

    /**
     * 返回 分数范围内 指定 count 数量的元素集合, 并且从 offset 下标开始(从大到小,不带分数的集合)
     * @param key 键
     * @param min 最小分值
     * @param max 最大分值
     * @param offset 从指定下标开始
     * @param count 输出指定元素数量
     */
    public Set<V> reverseRangeByScore(String key,double min,double max,long offset,long count){
        return zSetOperations.reverseRangeByScore(key, min, max, offset, count);
    }

    /**
     * 获取索引区间内的排序结果集合(从0开始,从大到小,带上分数)
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     */
    public Set<ZSetOperations.TypedTuple<V>> reverseRangeWithScores(String key, long start, long end){
        return zSetOperations.reverseRangeWithScores(key, start, end);
    }

    /**
     * 获取分数范围内的 [min,max] 的排序结果集合 (从大到小,集合带分数)
     * @param key 键
     * @param min 最小分值
     * @param max 最大分值
     */
    public Set<ZSetOperations.TypedTuple<V>> reverseRangeByScoreWithScores(String key, double min, double max){
        return zSetOperations.reverseRangeByScoreWithScores(key, min, max);
    }

    /**
     * 返回 分数范围内 指定 count 数量的元素集合, 并且从 offset 下标开始(从大到小,带分数的集合)
     * @param key 键
     * @param min 最小分值
     * @param max 最大分值
     * @param offset 从指定下标开始
     * @param count 输出指定元素数量
     */
    public Set<ZSetOperations.TypedTuple<V>> reverseRangeByScoreWithScores(String key, double min, double max, long offset, long count){
        return zSetOperations.reverseRangeByScoreWithScores(key, min, max, offset, count);
    }

    /**
     * 返回指定分数区间 [min,max] 的元素个数
     * @param key 键
     * @param min 最小分值
     * @param max 最大分值
     */
    public Long count(String key,double min,double max){
        return zSetOperations.count(key, min, max);
    }

    /**
     * 返回 zset 集合数量
     * @param key 键
     */
    public Long size(String key){
        return zSetOperations.size(key);
    }

    /**
     * 获取指定成员的 score 值
     * @param key 键
     * @param value 值
     */
    public Double score(String key, Object value){
        return zSetOperations.score(key, value);
    }

    /**
     * 删除一个或多个元素
     * @param key 键
     * @param values 值
     */
    public Long remove(String key, Object... values){
        return zSetOperations.remove(key, values);
    }

    /**
     * 删除指定索引位置的成员,其中成员分数按( 从小到大 )
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     */
    public Long removeRange(String key, long start, long end){
        return zSetOperations.removeRange(key, start, end);
    }

    /**
     * 删除指定 分数范围 内的成员 [main,max],其中成员分数按( 从小到大 )
     * @param key 键
     * @param min 最小分值
     * @param max 最大分值
     */
    public Long removeRangeByScore(String key,double min ,double max){
        return zSetOperations.removeRangeByScore(key, min, max);
    }

    /**
     *  key 和 other 两个集合的并集,保存在 destKey 集合中, 列名相同的 score 相加
     * @param key 源键1
     * @param otherKey 源键2
     * @param destKey 目标键
     */
    public Long unionAndStore(String key, String otherKey, String destKey){
        return zSetOperations.unionAndStore(key, otherKey, destKey);
    }

    /**
     *  key 和 otherKeys 多个集合的并集,保存在 destKey 集合中, 列名相同的 score 相加
     * @param key 源键1
     * @param otherKeys 源键2集合
     * @param destKey 目标键
     */
    public Long unionAndStore(String key, Collection<String> otherKeys, String destKey){
        return zSetOperations.unionAndStore(key, otherKeys, destKey);
    }

    /**
     *  key 和 otherKey 两个集合的交集,保存在 destKey 集合中
     * @param key 源键1
     * @param otherKey 源键2
     * @param destKey 目标键
     */
    public Long intersectAndStore(String key, String otherKey, String destKey){
        return zSetOperations.intersectAndStore(key, otherKey, destKey);
    }

    /**
     *  key 和 otherKeys 多个集合的交集,保存在 destKey 集合中
     * @param key 源键1
     * @param otherKeys 源键2集合
     * @param destKey 目标键
     */
    public Long intersectAndStore(String key, Collection<String> otherKeys, String destKey){
        return zSetOperations.intersectAndStore(key, otherKeys, destKey);
    }

    /**
     * 修改变量中的元素的分值
     * @param key 键
     * @param value 值
     * @param score 分值
     */
    public Double incrementScore(String key, V value, double score){
        return zSetOperations.incrementScore(key, value, score);
    }

    /**
     * 扫描指定key下的数据集
     * @param key 集合键
     * @param matchKey 匹配关键字 模糊查询：*123，*
     */
    public Set<ZSetOperations.TypedTuple<V>> scan(String key, String matchKey) {
        Set<ZSetOperations.TypedTuple<V>> data = new HashSet<>();
        try(Cursor<ZSetOperations.TypedTuple<V>> cursor = this.zSetOperations.scan(key, ScanOptions.scanOptions().match(matchKey).count(1000).build())) {
            while (cursor.hasNext()){
                ZSetOperations.TypedTuple<V> entry = cursor.next();
                data.add(entry);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return data;
    }
}
